﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Text;
using System.Xml;
using System.Windows.Forms;

namespace SdmxMl.Common
{
    /// <summary>
    /// Base Class to manage flat hierarchy (defined through Parent attribute)
    /// </summary>
    public class FlatHierarchicalList : List<ISdmxHierarchyListItem>, IHierarchyManage
    {
        #region Internal members

        private string colIdName;
        private string colDescriptionName;

        private bool hasHierarchy;

        #endregion

        #region Properties

        public bool HasHierarchy
        {
            get { return hasHierarchy; }
            set { hasHierarchy = value; }
        }
        #endregion

        #region C'tor

        public FlatHierarchicalList(string colId, string colDescr)
        {
            colIdName = colId;
            colDescriptionName = colDescr;
        }

        //Construct list content from XML document node
        public void ReadXml(XmlNode nodeArtefact, SdmxNamespaceManager nsmgr, string query)
        {
            Clear();

            XmlNodeList nodeList = nodeArtefact.SelectNodes(query, nsmgr);
            if (nodeList.Count > 0)
            {
                foreach (XmlNode nd in nodeList)
                {
                    ISdmxHierarchyListItem item = CreateNewItem(nd, nsmgr);
                    Add(item);
                    if (string.IsNullOrEmpty(item.Parent) == false)
                        HasHierarchy = true;
                }
            }
            
            /* To correct artefacts with remaining items without valid parent
             */
            List<ISdmxHierarchyListItem> listRemove = new List<ISdmxHierarchyListItem>();
            do
            {
                listRemove.Clear();
                // Clean non refered parent
                foreach (ISdmxHierarchyListItem item in this)
                {
                    if (string.IsNullOrEmpty(item.Parent) == false)
                    {
                        if (GetParent(item) == null)
                            listRemove.Add(item);
                    }
                }
                foreach (ISdmxHierarchyListItem itRemove in listRemove)
                    RemoveHierarchicalItem(itRemove);
            } while (listRemove.Count > 0);
            
        }

        #endregion

        #region Public Methods

        public ISdmxHierarchyListItem GetParent(ISdmxHierarchyListItem it)
        {
            ISdmxHierarchyListItem parent = null;
            if (string.IsNullOrEmpty(it.Parent) == false)
                foreach (ISdmxHierarchyListItem item in this)
                    if (item.Id == it.Parent)
                    {
                        parent = item;
                        break;
                    }

            return parent;
        }

        /// <summary>Remove proposed item and all its childs from the list</summary>
        /// <param name="it">the proposed item</param>
        public bool RemoveHierarchicalItem(ISdmxHierarchyListItem it)
        {
            List<ISdmxHierarchyListItem> lr = new List<ISdmxHierarchyListItem>(Count);
            foreach (ISdmxHierarchyListItem item in this)
                if (item == it || item.Parent == it.Id)
                    lr.Add(item);

            int nb = lr.Count;
            do{
                nb = lr.Count;
                foreach (ISdmxHierarchyListItem item in this)
                    if (string.IsNullOrEmpty(item.Parent)==false && ParentDeleted (lr, item))
                        lr.Add(item);
            }while (lr.Count > nb);



            foreach (ISdmxHierarchyListItem item in lr)
                this.Remove(item);

            return lr.Count > 0;
        }

        private bool ParentDeleted(List<ISdmxHierarchyListItem> lr, ISdmxHierarchyListItem target)
        {
            foreach (ISdmxHierarchyListItem item in lr)
                if (item.Id == target.Parent && lr.Contains(target) == false)
                    return true;
            return false;
        }

        /// <summary>
        /// Retrieves list of items that are direct child of submitted item id
        /// </summary>
        /// <param name="itemId">Parent id</param>
        /// <returns>a List</returns>
        public List<ISdmxHierarchyListItem> GetChildOfItem(string itemId)
        {
            List<ISdmxHierarchyListItem> list = new List<ISdmxHierarchyListItem>();
            foreach (ISdmxHierarchyListItem item in this)
                if (item.Parent == itemId)
                    list.Add(item);
            return list;
        }

        /// <summary>Rename proposed item and all its childs references</summary>
        /// <param name="it">the proposed item</param>
        public bool RenameHierarchicalItem(ISdmxHierarchyListItem it, string newName)
        {
            string oldName = it.Id;
            it.Id = newName;
            bool ret = CreateUniqueCode(it);
            foreach (ISdmxHierarchyListItem item in this)
                if (item.Parent == oldName)
                    item.Parent = it.Id;
            return ret;
        }

        /// <summary> Checks if code already used and if Yes, create a unique code for proposed item.</summary>
        /// <param name="it">the proposed item</param>
        /// <returns>True if parameter item Id already used by other item in the list</returns>
        public bool CreateUniqueCode(ISdmxHierarchyListItem it)
        {
            Dictionary<string, string> h = new Dictionary<string, string>();
            foreach (ISdmxHierarchyListItem item in this)
                if (item != it)
                {
                    if (h.ContainsKey(item.Id))
                        throw new ApplicationException("Item " + item.Id + " is duplicated!");
                    h.Add(item.Id, item.Id);
                }
            int nb = 1;
            bool alreadyDefined = false;
            while (h.ContainsKey(it.Id))
            {
                alreadyDefined = true;
                it.Id = "undefined" + nb.ToString();
                nb++;
            }
            return alreadyDefined;
        }

        private void ComposeChilds(List<ISdmxHierarchyListItem> listChilds, ISdmxHierarchyListItem parent)
        {
            List<ISdmxHierarchyListItem> listCur = new List<ISdmxHierarchyListItem>(50);
            listChilds.Add(parent);
            foreach (ISdmxHierarchyListItem it in this)
            {
                if (it.Parent == parent.Id)
                    listCur.Add(it);
            }
            foreach (ISdmxHierarchyListItem itChild in listCur)
                ComposeChilds(listChilds, itChild);
        }

        public void RedefineParent(ISdmxHierarchyListItem item, string newParentId, object newOwner)
        {
            item.Parent = newParentId;
        }
        #endregion

        #region Table Representation

        /// <summary>Provides list of possible parents for suggested item</summary>
        /// <param name="item">Suggested item</param>
        /// <returns>the list as a table with id and description name</returns>
        public DataTable PossibleParentsDataTable(ISdmxHierarchyListItem item)
        {
            DataTable dataTable = CreateTableContainer();

            List<ISdmxHierarchyListItem> listChilds = new List<ISdmxHierarchyListItem>(Count);
            List<ISdmxHierarchyListItem> listPossible = new List<ISdmxHierarchyListItem>(this);

            ComposeChilds(listChilds, item);
            foreach (ISdmxHierarchyListItem itChild in listChilds)
                listPossible.Remove(itChild);


            foreach (ISdmxHierarchyListItem it in listPossible)
                dataTable.Rows.Add(new object[] {it.Id, it.Name });

            return dataTable;
        }

        /// <summary>Create table container object</summary>
        /// <returns>The dataTable object</returns>
        private DataTable CreateTableContainer()
        {
            DataTable dataTable = new DataTable();
            dataTable.Columns.Add(new DataColumn(colIdName, typeof(string)));
            dataTable.Columns.Add(new DataColumn(colDescriptionName, typeof(string)));

            return dataTable;
        }

        public DataTable GetTableRepresentation()
        {
            DataTable dataTable = CreateTableContainer();
            foreach (ISdmxHierarchyListItem it in this)
                dataTable.Rows.Add(new object[] { it.Id, it.Name });
          //  dataTable.DefaultView.Sort = colIdName;

            return dataTable;
        }
        #endregion

        #region Tree support

        /// <summary>Creates flat or hierarchical hierarchy</summary>
        /// <param name="parentNode"></param> <returns></returns>
        public TreeNode FillTree(TreeNode parentNode)
        {
            if (hasHierarchy)
                CreateTreeHierarchy(parentNode);
            else
                CreateTreeFlat(parentNode);
            return parentNode;
        }

        /// <summary>Create list as flat</summary>
        /// <param name="parent">Parent node to attach list</param>
        /// <returns>parent node</returns>
        private TreeNode CreateTreeFlat(TreeNode parent)
        {
            foreach (ISdmxHierarchyListItem item in this)
                FillNode(item, parent);

            return parent;
        }


        private TreeNode CreateTreeHierarchy(TreeNode parent)
        {
            List<ISdmxHierarchyListItem> listRemain = new List<ISdmxHierarchyListItem>(this);
            List<ISdmxHierarchyListItem> listRemove = new List<ISdmxHierarchyListItem>(Count);

            // Attach root items
            foreach (ISdmxHierarchyListItem item in this)
            {
                if (string.IsNullOrEmpty(item.Parent))
                {
                    FillNode(item, parent);
                    listRemove.Add(item);
                }
            }

            foreach (ISdmxHierarchyListItem item in listRemove)
                listRemain.Remove(item);

            FilleLevel(parent, listRemain, listRemove);

            return parent;
        }

        private void FilleLevel(TreeNode parent, List<ISdmxHierarchyListItem> listRemain, List<ISdmxHierarchyListItem> listRemove)
        {
            listRemove.Clear();
            foreach (TreeNode n in parent.Nodes)
            {
                string parentId = (n.Tag as ISdmxHierarchyListItem).Id;
                // Attach root items
                foreach (ISdmxHierarchyListItem item in listRemain)
                {
                    if (item.Parent == parentId)
                    {
                        FillNode(item, n);
                        listRemove.Add(item);
                    }
                }
                foreach (ISdmxHierarchyListItem item in listRemove)
                    listRemain.Remove(item);

                if (n.Nodes.Count > 0)
                {
                    FilleLevel(n, listRemain, listRemove);
                }
            }
        }

        public TreeNode FillNode(ISdmxHierarchyListItem item, TreeNode parentNode)
        {
            return item.FillNode(parentNode);
        }


        public TreeNode NewChild(TreeNode node)
        {
            TreeNode t = node;
            if (node != null)
            {
                ISdmxHierarchyListItem parentItem = node.Tag as ISdmxHierarchyListItem;
                ISdmxHierarchyListItem item = CreateNewItem();
                CreateUniqueCode(item);
                Add(item);
                if (parentItem != null)
                {
                    item.Parent = parentItem.Id;
                    HasHierarchy = true;
                }

                t = FillNode(item, node);
            }
            return t;
        }
        #endregion

        #region Virtual

        public virtual ISdmxHierarchyListItem CreateNewItem()
        {
            // Implements in derived
            return null;
        }

        public virtual ISdmxHierarchyListItem CreateNewItem(XmlNode nd, SdmxNamespaceManager nsmgr)
        {
            // Implements in derived
            return null;
        }
        #endregion

        #region Item Search

        public ISdmxHierarchyListItem LocateItem(string itemId)
        {
            ISdmxHierarchyListItem ret = null;
            foreach (ISdmxHierarchyListItem item in this)
                if (item.Id == itemId)
            {
                ret = item;
                break;
            }
            return ret;
        }
        #endregion

        #region Sort

        /// <summary>Sort hierarchical items (detect numeric or ASCII)</summary>
        /// <param name="revert">it true apply revert sorting</param>
        public void Sort(bool revert)
        {
            // Creates a presentation of the hierarchy as tree
            TreeNode rootNode = new TreeNode();
            this.FillTree(rootNode);

            // Sort all levels of tree representation
            SortHierarchy(rootNode.Nodes, revert);

            // Now reorder SDMX container
            List<ISdmxHierarchyListItem> itemList = new List<ISdmxHierarchyListItem>();
            ReorderContainer(rootNode.Nodes, itemList);
            this.Clear();
            this.AddRange(itemList.ToArray());
        }

        // Recurse
        private void SortHierarchy(TreeNodeCollection nodes, bool revert)
        {
            List<string> codes = new List<string>();
            List<TreeNode> listNodes = new List<TreeNode>();
            TreeNode ParentNode = nodes.Count > 0 ? nodes[0].Parent : null;
            foreach (TreeNode node in nodes)
            {
                listNodes.Add(node);
                codes.Add(((ISdmxHierarchyListItem)node.Tag).Id);
                SortHierarchy(node.Nodes, revert);
            }
            bool numeric = SdmxMl.Helper.NumHelper.IsListNumeric(codes, System.Globalization.NumberStyles.Number);
            if (numeric)
            {
                listNodes.Sort(delegate(TreeNode c1, TreeNode c2)
                {
                    ISdmxHierarchyListItem it1 = c1.Tag as ISdmxHierarchyListItem;
                    ISdmxHierarchyListItem it2 = c2.Tag as ISdmxHierarchyListItem;
                    return (int)(Convert.ToInt64(it1.Id) - Convert.ToInt64(it2.Id));
                });
            }
            else
            {
                listNodes.Sort(delegate(TreeNode c1, TreeNode c2)
                {
                    ISdmxHierarchyListItem it1 = c1.Tag as ISdmxHierarchyListItem;
                    ISdmxHierarchyListItem it2 = c2.Tag as ISdmxHierarchyListItem;
                    return it1.Id.CompareTo(it2.Id);
                });
            }
            if (revert)
                listNodes.Reverse();
            // reorder nodes
            if (ParentNode != null)
            {
                ParentNode.Nodes.Clear();
                ParentNode.Nodes.AddRange(listNodes.ToArray());
            }
        }

        private void ReorderContainer(TreeNodeCollection nodes, List<ISdmxHierarchyListItem> items)
        {
            foreach (TreeNode node in nodes)
            {
                items.Add((ISdmxHierarchyListItem)node.Tag);
                ReorderContainer(node.Nodes, items);
            }
        }

        #endregion
    }
}
